package com.carhailing.coupons.service.impl;

import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.carhailing.common.constants.CouponsConstant;
import com.carhailing.common.constants.JwtConstants;
import com.carhailing.common.constants.TokenConstants;
import com.carhailing.common.domain.coupons.*;
import com.carhailing.common.domain.response.SysUserDto;
import com.carhailing.common.exception.BusinessException;
import com.carhailing.common.interfaces.Codes;
import com.carhailing.common.interfaces.Messages;
import com.carhailing.common.result.Result;
import com.carhailing.common.utils.JwtUtils;
import com.carhailing.coupons.constant.CouponsActiveConstant;
import com.carhailing.coupons.mapper.CouponScheduleMapper;
import com.carhailing.coupons.service.CouponScheduleService;
import com.carhailing.coupons.utils.CouponRedisLuaUtil;
import com.carhailing.coupons.utils.DateUtils;
import org.redisson.api.RLock;
import org.redisson.api.RSemaphore;
import org.redisson.api.RedissonClient;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.servlet.http.HttpServletRequest;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * @Author: Gd
 * @package: com.carhailing.coupons.service.impl
 * @Project: Online_Taxi_Project
 * @Name: CouponScheduleServiceImpl
 * @Date: 2024-7-25 15:12
 */
@Service
public class CouponScheduleServiceImpl extends ServiceImpl<CouponScheduleMapper, CouponScheduleDto> implements CouponScheduleService {


    @Autowired
    private CouponScheduleMapper couponScheduleMapper;

    @Autowired
    private RedisTemplate<String,String> redisTemplate;

    @Autowired
    private HttpServletRequest servletRequest;

    @Autowired
    private CouponRedisLuaUtil couponRedisLuaUtil;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Autowired
    private RedissonClient redissonClient;


    /**
     * 优惠券活动场次列表
     */
    @Override
    public List<CouponScheduleDto> selectCouponScheduleList(CouponScheduleDto couponScheduleDto) {
        List<CouponScheduleDto> couponScheduleDtoList = couponScheduleMapper.selectCouponScheduleList(couponScheduleDto);
        System.out.println(couponScheduleDtoList);
        return couponScheduleDtoList;
    }


    /**
     * 商家最近参与优惠券抢券活动的优惠券信息
     */
    @Override
    public void uploadCouponsActiveScheduled() {
        // 查询最近参与优惠券抢券活动的  场次 + 优惠券信息
        CouponsAndCouponsSchedule couponsAndCouponsSchedule = this.couponScheduleListByDateSession();
        System.out.println(couponsAndCouponsSchedule);
        if (couponsAndCouponsSchedule != null) {
            // 获取优惠券场次
            List<CouponScheduleDto> couponScheduleDtos = couponsAndCouponsSchedule.getCouponScheduleDtos();
            // 优惠券场次上架信息
            uploadCouponScheduleDtoInfo(couponScheduleDtos);
            System.out.println("优惠券场次信息：" + couponScheduleDtos);
            // 获取优惠券模板
            List<CouponsVo> couponsVos = couponsAndCouponsSchedule.getCouponsVos();
            // 优惠券模板上架信息
            uploadCouponsVoInfo(couponsVos);
            System.out.println("优惠券模板信息：" + couponsVos);
        }
    }

    /**
     * 查询当前时间可以参与秒杀的优惠券
     */
    @Override
    public List<CouponsAndCouponsSchedule> getCurrentSeckillCoupons() throws InterruptedException {
        // 获取用户信息   Authorization
        Result<SysUserDto> info = this.info();
        SysUserDto user = info.getData();
        // 查询当前时间所属的优惠券场次
        long currentTimeMillis = System.currentTimeMillis();
        // 拿到优惠券场次信息
        CouponsAndCouponsSchedule couponsAndCouponsSchedule = this.couponScheduleListByDateSession();
        List<CouponScheduleDto> couponScheduleDtos = couponsAndCouponsSchedule.getCouponScheduleDtos();
        if(!CollectionUtils.isEmpty(couponScheduleDtos)){

            for (CouponScheduleDto couponScheduleDto : couponScheduleDtos) {
                // 获取场次开始时间
                String couponScheduleStartTime = couponScheduleDto.getCouponScheduleStartTime();
                // 使用 Hutool 的 DateUtil 将 Date 转换为时间戳（毫秒值）
                long start = DateUtil.parse(couponScheduleStartTime).getTime();
                // 获取场次结束时间
                String couponScheduleEndTime = couponScheduleDto.getCouponScheduleEndTime();
                long end = DateUtil.parse(couponScheduleEndTime).getTime();
                // 判断该场次
                if(currentTimeMillis >= start && currentTimeMillis <= end){

                    CouponsVo couponsVo = couponScheduleMapper.selectCouponsInfos(couponScheduleDto.getCouponScheduleStartTime());

                    // 根据优惠券场次id查询优惠券活动信息
//                    CouponsActiveVo couponsActiveVo = couponScheduleMapper.selectCouponsActiveInfo(couponScheduleDto.getCouponScheduleId());
//                    System.out.println("优惠券活动信息：" + couponsActiveVo);
//                    CouponsVo couponsVo = couponScheduleMapper.selectCoupons(couponsActiveVo.getCouponId());
                    if(null != couponsVo && couponsVo.getCouponsTotal() > 0 && couponsVo.getCouponsStatus() == 0){
                        // 拿到前台token令牌
                        String uuidToken = couponsVo.getToken();
                        String token = redisTemplate.opsForValue().get(uuidToken);
//                        String token = redisTemplate.opsForValue().get("93396569-15b9-4e79-8eb2-350ee0312a09");
//                         比较 token
//                         判断redis中是否存在token令牌  存在  该用户不可重复抢券
                        if(!redisTemplate.hasKey(token)){
                            try {
                                throw new BusinessException(Codes.TOKEN_ALREADY_EXISTS,Messages.TOKEN_ALREADY_EXISTS);
                            } catch (BusinessException e) {
                                throw new RuntimeException(e);
                            }
                        }
//                         删除令牌
                        redisTemplate.delete(token);
//                         领取优惠券，received
//                        boolean received =couponRedisLuaUtil.receive(couponsVo);

                        // 分布式锁  解决优惠券重复领取问题   用户是否已领取成功
                        // 设置锁
//                        RLock lock = redissonClient.getLock(CouponsConstant.COUPONS_STOCK_SEMAPHORE + 1);
                        RLock lock = redissonClient.getLock(CouponsConstant.COUPONS_STOCK_SEMAPHORE + user.getUserid());
                        try {
                            boolean tryLock = lock.tryLock();
                            if(!tryLock){
                                throw new RuntimeException("锁被占用");
                            }
                        }catch (Exception e){
                            e.printStackTrace();
                        } finally {
                            // 当前是否有锁
                            if(lock.isLocked()){
                                // 该线程是否有锁
                                if(lock.isHeldByCurrentThread()){
                                    // 释放锁
                                    lock.unlock();
                                }
                            }
                        }
                        // 添加用户优惠券信息， 原子性
                        if(couponsVo.getCouponsTotal() > 0 ){
                            // 扣减库存
                            Integer integer = couponScheduleMapper.updateCouponsTotal(couponsVo.getCouponId());
                            if(integer > 0){
                                // 创建优惠券用户记录表
                                CouponUseRecord couponUseRecord = new CouponUseRecord();
//                            couponUseRecord.setUserId(Long.valueOf(user.getUserid()));
                                couponUseRecord.setUserId(1L);
//                            couponUseRecord.setUserName(user.getUsername());
                                couponUseRecord.setUserName("张三");
                                couponUseRecord.setRecordDate(new Date());
                                couponUseRecord.setCouponId(Long.valueOf(couponsVo.getCouponId()));
                                // 异步发放优惠券
                                rabbitTemplate.convertAndSend("user_coupons_revice", couponUseRecord, message -> {
                                    message.getMessageProperties().setMessageId(UUID.randomUUID().toString());
                                    return message;
                                });
                            }
                        }else {
                            try {
                                throw new BusinessException(Codes.RECEIVE_COUPONS_FAIL, Messages.RECEIVE_COUPONS_FAIL);
                            } catch (BusinessException e) {
                                throw new RuntimeException(e);
                            }
                        }
                    }
                }
            }

//            couponScheduleDtos.forEach(couponScheduleDto -> {
//
//            });
        }
        return null;
    }

    /**
     * 获取token令牌
     */
    @Override
    public Result getToken() {
        // 校验用户是否重复下单 (幂等性)
        // 生成 UUID 作为令牌
        String token = UUID.randomUUID().toString();
        CouponsVo couponsVo = new CouponsVo();
        couponsVo.setToken(token);
        // 将当前秒杀的用户token存入缓存  判断幂等性问题
        redisTemplate.opsForValue().set(token,token,1, TimeUnit.HOURS);
        return Result.success(couponsVo, "获取token令牌");
    }

    /**
     * 异步记录抢券记录
     */
    @Override
    public void insertCouponUseRecord(CouponUseRecord couponUseRecord) {
        couponScheduleMapper.insertCouponUseRecord(couponUseRecord);
    }

    /**
     * 获取用户信息
     */
    public Result<SysUserDto> info() {
        String token = servletRequest.getHeader(TokenConstants.TOKEN);
        String userKey = JwtUtils.getUserKey(token);
        String user = redisTemplate.opsForValue().get(JwtConstants.USER_KEY + userKey);
        SysUserDto user1 = JSON.parseObject(user, SysUserDto.class);
        return Result.success(user1);
    }

    /**
     * 优惠券模板上架信息
     */
    private void uploadCouponsVoInfo(List<CouponsVo> couponsVos) {
        if(!CollectionUtils.isEmpty(couponsVos)){
            // 优惠券模板信息
            couponsVos.stream().forEach(couponsVo -> {
               //  判断优惠券是否已上架（幂等性）
                String couponsKey = CouponsActiveConstant.COUPONS_KEY + couponsVo.getCouponId();
                Boolean key = redisTemplate.hasKey(couponsKey);
                if(!key){
                    redisTemplate.opsForList().leftPushAll(couponsKey, JSONObject.toJSONString(couponsVo));
                }
            });
        }
    }

    /**
     * 优惠券场次上架信息
     */
    private void uploadCouponScheduleDtoInfo(List<CouponScheduleDto> couponScheduleDtos) {

        if(!CollectionUtils.isEmpty(couponScheduleDtos)){
            couponScheduleDtos.stream().forEach(couponScheduleDto -> {
                Long couponScheduleId = couponScheduleDto.getCouponScheduleId();
                String couponsScheduleKey = CouponsActiveConstant.COUPONS_SCHEDULED + couponScheduleId;
                // 判断场次的幂等性问题  避免重复上架优惠券场次
                Boolean key = redisTemplate.hasKey(couponsScheduleKey);
                if(!key){
                    redisTemplate.opsForList().leftPushAll(couponsScheduleKey, JSONObject.toJSONString(couponScheduleDto));
                }
            });
        }
    }

    /**
     * 查询最近三天参与优惠券抢券的场次 + 优惠券信息
     */
    public CouponsAndCouponsSchedule couponScheduleListByDateSession() {
        // 创建一个存放 优惠券  优惠券场次 的对象
        CouponsAndCouponsSchedule couponsAndCouponsSchedule = new CouponsAndCouponsSchedule();
        // 计算最近三天起止时间
        String startTime = DateUtils.currentStartTime();
        System.out.println(startTime);
        String endTime = DateUtils.getTimeByOfferset(2);
        System.out.println(endTime);
        // 查询以上起止时间内参与优惠券抢券活动的优惠券场次信息
        List<CouponScheduleDto> sessions = baseMapper.selectList(new QueryWrapper<CouponScheduleDto>()
                .between("coupon_schedule_start_time", startTime, endTime));
//        System.out.println(sessions);
        couponsAndCouponsSchedule.setCouponScheduleDtos(sessions);
        // 优惠券场次关联的优惠券模板信息
        if(!CollectionUtils.isEmpty(sessions)){
            // 遍历优惠券场次信息
            sessions.forEach(session -> {
                    String couponScheduleStartTime = session.getCouponScheduleStartTime();
                    String couponScheduleEndTime = session.getCouponScheduleEndTime();
                    // 查询该场次下的优惠券模板信息
                    List<CouponsVo> couponsVos = couponScheduleMapper.selectCouponsInfo(couponScheduleStartTime,couponScheduleEndTime);
                    couponsVos.forEach(couponsVo -> {
                        if(couponsVo.getCouponsTotal().equals(0)){
                            // 优惠券总数已经发放结束  停止发放
                            couponScheduleMapper.updateCouponsStatus(couponsVo.getCouponId());
                        }
                    });
//                System.out.println(couponsVos);
                    couponsAndCouponsSchedule.setCouponsVos(couponsVos);
            });
        }
//        System.out.println(couponsAndCouponsSchedule);
        return couponsAndCouponsSchedule;
    }


}
